package com.example.imagecompressor.service;

import com.example.imagecompressor.dto.ProcessingResult;
import com.example.imagecompressor.model.OutputFormat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.concurrent.CompletableFuture;

@Service
public class AsyncImageProcessingWrapper {

    private static final Logger logger = LoggerFactory.getLogger(AsyncImageProcessingWrapper.class);
    private final ImageProcessingService imageProcessingService;
    private final Path outputDirectory; // Directory to store compressed files

    @Autowired
    public AsyncImageProcessingWrapper(ImageProcessingService imageProcessingService) {
        this.imageProcessingService = imageProcessingService;
        // Define the output directory (e.g., 'output-images' in the working directory)
        // In a real application, this should be configurable
        this.outputDirectory = Paths.get("output-images");
        createOutputDirectoryIfNotExists();
    }

    private void createOutputDirectoryIfNotExists() {
        try {
            if (!Files.exists(outputDirectory)) {
                Files.createDirectories(outputDirectory);
                logger.info("Created output directory: {}", outputDirectory.toAbsolutePath());
            }
        } catch (IOException e) {
            logger.error("Could not create output directory: {}", outputDirectory.toAbsolutePath(), e);
            // Handle error appropriately - maybe throw a runtime exception?
        }
    }

    @Async // Marks this method for asynchronous execution
    public CompletableFuture<ProcessingResult> processFileAsync(MultipartFile file, OutputFormat format) {
        String originalFilename = file.getOriginalFilename() != null ? file.getOriginalFilename() : "unknown_file";
        ProcessingResult result;

        try {
            byte[] compressedData = imageProcessingService.processImage(file, format);

            if (compressedData != null) {
                // Generate a safe filename for storage
                String outputFilename = imageProcessingService.generateOutputFilename(originalFilename, format);
                Path outputPath = outputDirectory.resolve(outputFilename);

                // Handle potential filename collisions - simple approach: append counter
                int counter = 1;
                String baseName = outputFilename.substring(0, outputFilename.lastIndexOf('.'));
                String extension = outputFilename.substring(outputFilename.lastIndexOf('.'));
                while (Files.exists(outputPath) && counter < 100) { // Avoid infinite loop
                    outputFilename = String.format("%s_%d%s", baseName, counter++, extension);
                    outputPath = outputDirectory.resolve(outputFilename);
                }
                if (Files.exists(outputPath)) {
                     logger.error("Failed to find unique output filename for base {} after {} attempts.", baseName, counter);
                     result = ProcessingResult.failure(originalFilename, "Failed to save file: Could not generate unique filename.");
                     return CompletableFuture.completedFuture(result);
                }

                // Save the compressed data
                Files.write(outputPath, compressedData);
                logger.info("Saved compressed file {} to {}", outputFilename, outputPath.toAbsolutePath());
                result = ProcessingResult.success(originalFilename, outputFilename, compressedData.length);
            } else {
                // processImage returned null, meaning compression failed or target size not met
                 result = ProcessingResult.failure(originalFilename, "Compression failed or could not meet target size (< 2MB).");
            }

        } catch (IOException | InterruptedException e) {
            logger.error("Error processing file {}: {}", originalFilename, e.getMessage(), e);
            // Handle specific exceptions differently? e.g. InterruptedException
             if (e instanceof InterruptedException) {
                 Thread.currentThread().interrupt(); // Re-interrupt the thread
                 result = ProcessingResult.failure(originalFilename, "Processing was interrupted.");
             } else {
                result = ProcessingResult.failure(originalFilename, "Processing error: " + e.getMessage());
             }
        } catch (Exception e) {
             // Catch any other unexpected errors
             logger.error("Unexpected error processing file {}: {}", originalFilename, e.getMessage(), e);
             result = ProcessingResult.failure(originalFilename, "Unexpected processing error.");
        }

        return CompletableFuture.completedFuture(result);
    }
} 